package runj

import (
	"bytes"
	context2 "context"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/url"
	"strings"

	"gitee.com/knowgo/rjhttp/pkg/rjh"

	"gitee.com/knowgo/runjson/pkg/context"

	"gitee.com/knowgo/runjson/pkg/rj"

	"gitee.com/knowgo/runjson"
)

// RjHandler 处理 runjson 请求
type RjHandler struct {
	Runner  *runjson.Runner
	Option  *rjh.Option
	parseFn func(request *http.Request, option *rjh.Option) (rj.Requests, error)
}

// injectUpload 上传辅助类注入函数
func injectUpload(arg *rj.InjectArg) (*rjh.Upload, error) {
	request := rjh.ParseRequest(arg.Args)
	return &rjh.Upload{Request: request}, nil
}

func injectExtra(arg *rj.InjectArg) (rjh.Extra, error) {
	extra := rjh.ParseExtra(arg.Args)
	return extra, nil
}

func injectShuttlecraft(arg *rj.InjectArg) (rjh.Shuttlecraft, error) {
	return rjh.ParseShuttlecraft(arg.Args), nil
}

// NewRjHandler 创建 runjson handler
func NewRjHandler(runner *runjson.Runner, opt *rjh.Option) *RjHandler {
	// 注入额外数据提供者
	if err := runner.RegisterProvider(injectExtra); err != nil {
		panic(err)
	}
	// 注入数据传送者
	if err := runner.RegisterProvider(injectShuttlecraft); err != nil {
		panic(err)
	}
	if opt.EnableUpload {
		// 注入上传文件操作结构体
		if err := runner.RegisterProvider(injectUpload); err != nil {
			panic(err)
		}
		// 可以上传文件
		return &RjHandler{
			Runner:  runner,
			Option:  opt,
			parseFn: parseFieldOrBody,
		}
	}
	return &RjHandler{
		Runner:  runner,
		Option:  opt,
		parseFn: parseBody,
	}

}
func parseFieldOrBody(request *http.Request, option *rjh.Option) (rj.Requests, error) {
	// http POST 判断是否有指定参数 field 名称
	//	如果有则使用 PostForm 中的此值作为请求参数
	//	如果没有，则使用 body 作为请求参数

	fieldName := request.Header.Get(rjh.PostFieldNameInHTTPHeader)
	if fieldName == "" {
		return parseBody(request, option) // option.UploadMaxBytes, logger)
	}
	var reqs rj.Requests

	if err := request.ParseMultipartForm(option.UploadMaxBytes); err != nil {
		return nil, err
	}

	//request.ParseMultipartForm(1000000)
	//val := request.PostForm.Get(fieldName)
	val := request.FormValue(fieldName)
	if val == "" {
		// 此处错误不作处理
		return nil, fmt.Errorf("no request found")
	}
	if option.OnRecv != nil {
		option.OnRecv(val)
	}
	d := json.NewDecoder(strings.NewReader(val))
	// d.UseNumber()
	if err := d.Decode(&reqs); err != nil {
		if option.OnError != nil {
			return nil, option.OnError(rjh.ErrRJHttp{Err: err, Progress: rjh.EP_ParseRequestURL})
		}
		return nil, err
	}
	return reqs, nil
}

func parseBody(request *http.Request, option *rjh.Option) (rj.Requests, error) {
	// http POST body 作为请求参数
	var reqs rj.Requests
	// data, err := ioutil.ReadAll(request.Body)
	// if err != nil {}
	// if logger != nil {
	data, err := ioutil.ReadAll(request.Body)
	if err != nil {
		// errors
		if option.OnError != nil {
			return nil, option.OnError(rjh.ErrRJHttp{Err: err, Progress: rjh.EP_HTTP})
		}
		return nil, fmt.Errorf("read http post body error: %w", err)
	}
	// val := string(data)
	if option.OnRecv != nil {
		option.OnRecv(string(data))
	}
	// logger.Infof("recv request:\n%s", val)
	d := json.NewDecoder(bytes.NewReader(data))
	// d.UseNumber()
	if err := d.Decode(&reqs); err != nil {
		if option.OnError != nil {
			return nil, option.OnError(rjh.ErrRJHttp{Err: err, Progress: rjh.EP_ParseRequestBody})
		}
		return nil, fmt.Errorf("parse body error, maybe need call rjhttp.EnableUpload(true): %w", err)
		// errors.Wrap(err, "Parse body error, maybe need call rjhttp.EnableUpload(true)")
	}
	// } else {
	// 	d := json.NewDecoder(request.Body)
	// 	// d.UseNumber()
	// 	if err := d.Decode(&reqs); err != nil {
	// 		return nil, fmt.Errorf("parse body error, maybe need call rjhttp.EnableUpload(true): %w", err)
	// 		// errors.Wrap(err, "Parse body error, maybe need call rjhttp.EnableUpload(true)")
	// 	}
	// }
	return reqs, nil
}

func parseQuery(request *http.Request) (rj.Requests, error) {
	// http get 请求，url 的 "?" 之后为请求内容
	// ?[{"service": "demo1.Test1", "args":{"id":1}}]
	body := request.RequestURI
	p := strings.Index(body, "?")
	if p < 0 {
		return nil, fmt.Errorf("no request param found")
	}
	query, err := url.QueryUnescape(body[p+1:])
	//query, err := url.ParseQuery(request.URL.RawQuery)
	if err != nil {
		return nil, fmt.Errorf("request param Unescape error")
	}
	if query == "" {
		// 查询字符串是空
		return nil, fmt.Errorf("no request param found")
	}
	var reqs rj.Requests
	d := json.NewDecoder(strings.NewReader(query))
	// d.UseNumber()
	if err := d.Decode(&reqs); err != nil {
		return nil, err
	}
	return reqs, nil
}

func (r *RjHandler) ServeHTTP(writer http.ResponseWriter, request *http.Request) {
	if r.Option.BeforeExecute != nil {
		if err := r.Option.BeforeExecute(writer, request); err != nil {
			return
		}
	}

	res := rjh.RjResponse{}
	// 首先解析 url ？后面的参数，如果是合法的 service 请求，则直接使用
	reqs, err := parseQuery(request)
	// http POST PUT
	if err != nil {
		// 从 post body 中解析 service 请求
		reqs, err = r.parseFn(request, r.Option) // r.Option.UploadMaxBytes, logger)
		if err != nil {
			// 解析请求时发生错误
			res.Status = rjh.StatusIllegalArgument
			res.StatusText = err.Error()
		}
	}
	// 执行 runjson 请求
	response, err := r.Runner.RunRequests(&context.Context{
		Context: context2.Background(),
		Param: map[string]interface{}{
			rjh.RequestField: request,
			rjh.WriterField:  writer,
			rjh.ExtraField:   &res,
			rjh.MaxSizeField: r.Option.UploadMaxBytes,
		},
	}, reqs)
	if err != nil {
		// 业务逻辑发生错误
		switch real := err.(type) {
		case *runjson.ErrRequstServiceDuplicate:
			// 请求参数中出现了 2 个相同名称的 service api
			res.Error = real.Err
			res.Status = rjh.StatusIllegalArgument
		case *runjson.ErrAccessDeny:
			// 不能通过检查
			if ewc, ok := real.Err.(*rjh.ErrorWithStatus); ok {
				res.Status = ewc.Status
				res.Error = ewc.Err
			} else {
				res.Error = real.Err
				res.Status = rjh.StatusPermissionDeny
			}
		case *runjson.ErrServiceNotFoundError:
			// 找不到服务
			res.Error = real.Err
			res.Status = rjh.StatusServiceNotFound
		default:
			// 业务逻辑中返回的错误
			if ewc, ok := err.(*rjh.ErrorWithStatus); ok {
				res.Status = ewc.Status
				res.Error = ewc.Err
			} else {
				res.Error = err
				res.Status = rjh.StatusCustom
			}
		}

		res.StatusText = err.Error()
	} else {
		// 成功
		if len(response) == 1 {
			// 如果是单独的 runjson
			for _, obj := range response {
				// 如果返回的是 RjBinary 对象，说明在业务逻辑函数内部已经处理了 writer 操作
				// 此处不再需要返回任何信息，直接 return
				if rjh.IsBinary(obj.DataType) {
					// 不做返回操作
					return
				}
			}
		}

		var responseData = map[string]interface{}{}
		for svc, obj := range response {
			if obj.Error != nil {
				res.Error = obj.Error.GetError()
				res.StatusText = res.Error.Error()
				res.Status = rjh.StatusCustom
				break
			}
			responseData[svc] = obj.Data
		}
		res.Data.Response = responseData
	}

	if r.Option.AfterExecute != nil {
		r.Option.AfterExecute(rjh.RJHContext{Writer: writer, Request: request, Requests: &reqs, Response: &res})
	}
	// 序列化 json
	// json.NewDecoder(nil).UseNumber()
	data, err := json.Marshal(res)
	if err != nil {
		// 序列化数据时发生错误
		if r.Option.OnError != nil {
			// 使用新的错误信息
			err = r.Option.OnError(rjh.ErrRJHttp{Err: err, Progress: rjh.EP_SerializeResponse})
		}
		res.Status = rjh.StatusResponseSerialize
		res.StatusText = err.Error()
		res.Data.Response = nil
		data, _ := json.Marshal(res)
		writer.Write(data)
	} else {
		// 返回数据
		writer.Write(data)
	}
}
